
local dbg = require 'jass.debug'
local jass = require 'jass.common'
local runtime = require 'jass.runtime'
local japi = require 'jass.japi'
local unit_class = ac.unit
local table = table
local math = math


local buff = {}
---@class ac.buff
local mt = {}
setmetatable(buff, buff)

buff.__index = mt

-- 类型
mt.type = 'buff'    

-- 名字
mt.name = ''

---@type ac.unit
-- 来源
mt.source = nil

---@type ac.unit
-- 所有者
mt.owner = nil

-- 共存方式(0 = 不共存,1 = 共存)
mt.cover_type = 0

-- 最大生效数量(仅用于共存的Buff)
mt.cover_max = 0

-- 本次持续时间
mt.time = -1

-- 总持续时间
mt.life_time = 0

-- 周期
mt.pulse = nil

mt.cycle_timer = nil

-- 已经循环的次数
mt.pulse_count = 0

-- 添加时间
mt.add_time = 0

-- 关联计时器
mt._timer = nil

-- buff说明
mt.tip = nil

-- 是否是正常到期而移除
mt.is_finish = false

-- 禁用计数
mt.disable_count = 0

-- 暂停计数
mt.pause_count = 0

-- 无视暂停
mt.force = false

---@type ac.skill
--技能
mt.skill = nil 

--添加buff的时间，ac.clock()
mt.__add_time = 0

--虚拟buff (notify对虚拟buff无效)
mt.virtual = false

--- 执行事件
function mt:notify(name,...)
	-- 虚拟buff则跳过
    if self.virtual then
        return false
    end
    local func = self[name]
    if func then 
        return select(2, xpcall(func, runtime.error_handle, self, ...))
    end 
end 


-- 暂停
function mt:pause(flag)
	if self.force then
		return
	end
	if flag == nil then
		flag = true
	end
	if flag then
		self.pause_count = self.pause_count + 1
		if self.pause_count == 1 then
			if self._timer then
				self._timer:pause()
			end
			if self.cycle_timer then
				self.cycle_timer:pause()
			end
		end
	else
		self.pause_count = self.pause_count - 1
		if self.pause_count == 0 then
			if self._timer then
				self._timer:resume()
			end
			if self.cycle_timer then
				self.cycle_timer:resume()
			end
		end
	end
end

-- 设置 Buff 时间
--	时间
function mt:set_remaining(time_out)
	if time_out < 0 then
		return
	end
	local target_time = ac.clock() / 1000 + time_out
	self.life_time = target_time - self.add_time
	if self._timer then
		self._timer:remove()
	end
	self._timer = self.owner:wait(time_out * 1000, function()
		-- 是否需要补上一跳(计时器误差)
		if self.pulse then
			local target_pulse_count = self.life_time / self.pulse
			if target_pulse_count - 1 == self.pulse_count then
				self.pulse_count = self.pulse_count + 1
				--log.error('buff on_finish')
				if self.on_pulse then
					self:on_pulse(self.pulse_count)
				end
			end
		end
		self.is_finish = true
		if self.on_finish then
			self:on_finish()
		end
		self:remove()
	end)
	if self._show_skill and self._show_skill._show_buff == self then
		self._show_skill:show_buff(self)
	end
	ac.game:notify('单位-刷新buff时间',self.owner,self)
end

-- 获得剩余时间
function mt:get_remaining()
	return math.max(0, self.life_time + self.add_time - ac.clock() / 1000)
end

function mt:set_time(time)
	self.time = time
	if self._show_skill and self._show_skill._show_buff == self then
		self._show_skill:show_buff(self)
	end
end

function mt:get_time()
	return self.time
end

-- 获得当前循环剩余时间
function mt:get_pulse()
	return self.cycle_timer:get_remaining()
end

-- 设置循环周期
--	周期
function mt:set_pulse(pulse)
	self.pulse = pulse
end



-- 移除 buff
function mt:remove()
	if self.removed then
		return
	end

	self.removed = true
	self.owner:notify('单位-失去状态', self.owner, self)
	if self._timer then
		self._timer:remove()
		self._timer = nil 
	end

	self:clear_effect()

	if self.cycle_timer then
		self.cycle_timer:remove()
		self.cycle_timer = nil
	end

	if self._show_skill and self._show_skill._show_buff == self then
		self._show_skill:show_buff(nil)
		self._show_skill = nil
	end
	
	--print('buff移除', self.name)
	self.owner.buffs[self] = nil
	local new_buff
	if self.cover_type == 1 then
		-- 可以共存的 buff，查表
		if self.owner.buff_list and self.owner.buff_list[self.name] then
			local list = self.owner.buff_list[self.name]
			for i = 1, #list do
				if self == list[i] then
					table.remove(list, i)
					-- 进入有效区的 buff 生效
					if self.cover_max >= i then
						new_buff = list[self.cover_max]
					end
					break
				end
			end
		end
	end
	if self:is_enable() and self.added and self.has_on_add then
		--自动清除的东西
		self:gc_clear()

		self.added = nil
		self.has_on_add = nil
		if self.on_remove then
			xpcall(self.on_remove, runtime['error_handle'], self, new_buff)
		end
	end
	if new_buff then
		self.owner:notify('单位-获得状态', self.owner, new_buff)
		new_buff:enable()
	end
	self.is_finish = false
	self.owner:notify('单位-失去状态ui', self.owner, self)
end

function mt:gc_clear()
	if self._gc_target then
		for k,v in ipairs(self._gc_target) do
			--function针对属性，调用后还原
			if type(v) == 'function' then
				v()
			else
				--remove针对计时器、触发、特效等，调用后删除
				v:remove()
			end
		end
		self._gc_target = nil
	end
end

-- buff 获得时的回调
mt.on_add = nil

-- buff 失去时的回调
mt.on_remove = nil

-- buff 正常到期时的回调
mt.on_finish = nil

-- buff 在每个周期时的回调
mt.on_pulse = nil

-- 覆盖同名 buff
mt.on_cover = nil

local gchash = 0

function mt:init_effect()
	if self.effect_data then
		local effect_only_one = self.effect_only_one
		local hero = self.owner
		local list = {}
		for k,v in sortpairs(self.effect_data) do
			if type(v) == 'table' then
				for o,e in sortpairs (v) do
					table.insert(list, hero:add_effect(e,k, nil, effect_only_one))
				end
			else
				table.insert(list, hero:add_effect(v, k, nil, effect_only_one))
			end
		end

		self.common_effect_list = list
	end
end 

function mt:clear_effect()
	local list = self.common_effect_list
	if list then
		if type(list) == 'table' then
			for k,v in sortpairs(list) do
				if v then
					v:remove()
				end
			end
		else
			if list then
				list:remove()
			end
		end
	end
end


-- 添加 buff
function unit_class.__index:add_buff(name, delay)
	return function(bff)
		if self.removed then 
			return 
		end 
		local class = ac.buff[name]

		if not class then
			log.error('未找到buff', name)
			return
		end


		bff.name = name

		local buff_data = ac.lni('buff',name)
		if buff_data then 
			for k, v in pairs(buff_data) do 
				bff[k] = v 
			end 
			bff.data = buff_data
		end

		gchash = gchash + 1
		dbg.gchash(bff, gchash)
		bff.gchash = gchash
		setmetatable(bff, {__index = class})
		if not self.buffs then
			self.buffs = {}
		end
		

		-- 初始化数据
		bff.name = bff.name or name
		bff.owner = self
		bff.target = bff.target or self
		
		--如果有tip，则自动匹配上title，和skill的art
		if bff.tip then
			if not bff.title then
				bff.title = name
			end
			if not bff.art and bff.skill then
				if type(bff.skill.art) == 'table' then
					bff.art = bff.skill.art[bff.skill:get_level()]
				else
					bff.art = bff.skill.art
				end
			end
		end

		if not bff.source then
			bff.source = self.skill and self.skill.owner or self
		end
		if delay then
			ac._timer(delay * 1000, 1, function()
				if bff.removed then
					return
				end
				bff:add()
			end)
			return bff
		else
			return bff:add()
		end
	end
end

function mt:add()
	if self.removed or self.added then
		return
	end
	self.added = true
	local name = self.name
	
	self.source = self.source

	if not self.owner:is_alive() and not self.keep then
		return
	end

	if self.on_init then
		self:on_init()
	end
    
	self.add_time = ac.clock() / 1000

	if self.owner:dispatch('单位-拦截获得状态', self.owner, self) then
		return
	end

	if self.cover_type == 0 then
		-- 不可共存的 buff，只会有一个，直接找到即可
		local this_buff = self.owner:find_buff(name)
		if this_buff then
			local result = true
			if this_buff.on_cover then
				result = this_buff:on_cover(self)
			end
			-- true 表示新 buff 覆盖，false 表示新 buff 添加失败，默认 true
			if result then
				this_buff:remove()
			else
				return
			end
		end
	elseif self.cover_type == 1 then
		-- 可以共存的 buff，查表
		if not self.owner.buff_list then
			self.owner.buff_list = {}
		end
		if not self.owner.buff_list[name] then
			self.owner.buff_list[name] = {}
		end
		local list = self.owner.buff_list[name]
		for i = 1, #list + 1 do
			local this_buff = list[i]
			if not this_buff then
				-- 没有其他 buff 了，就放这吧
				table.insert(list, i, self)
				-- 如果自己不在有效区内，则禁用
				if self.cover_max ~= 0 and i > self.cover_max then
					self:disable()
				end
				break
			end
			local result = false
			if this_buff.on_cover then
				result = this_buff:on_cover(self)
			end
			-- true 表示插入到当前位置，否则继续询问
			if result then
				table.insert(list, i, self)
				-- 如果刚好把原来的 buff 挤出有效区，则禁用他
				if self.cover_max == i then
					this_buff:disable()
				end
				-- 如果自己不在有效区内，则禁用
				if self.cover_max ~= 0 and i > self.cover_max then
					self:disable()
				end
				break
			end
		end
	end

	self.owner.buffs[self] = true

	-- 开启计时器
	if self.pulse then
		local last_pulse
		local function pulse()
			last_pulse = self.pulse
			self.cycle_timer = self.owner:loop(last_pulse * 1000, function(t)
				if self.owner then
					--print('buff周期', self.name)
					self.pulse_count = self.pulse_count + 1
					if self.on_pulse and self:is_enable() then
						self:on_pulse(self.pulse_count)
					end
					
					if self.pulse ~= last_pulse then
						self.cycle_timer = nil
						t:remove()
						if not self.removed then
							pulse()
						end
					end

				else
					self.cycle_timer = nil
					t:remove()
				end
			end)
		end
		
		pulse()
	end

	if self.time >= 0 then
		self:set_remaining(self.time)
	end

	self.removed = nil

	if self:is_enable() then
		if not self.has_on_add then
			self.has_on_add = true
			if self.on_add then
				self:on_add()
			end

			self:init_effect()
		end

		if self.removed then
			return
		end

		self.owner:notify('单位-获得状态', self.owner, self)
	end

	
	return self
end

function mt:is_enable()
	return self.disable_count == 0
end

function mt:enable()
	if not self.owner:is_alive() and not self.keep then
		return
	end
	self.disable_count = self.disable_count - 1
	if not self.removed and self.added and self.disable_count == 0 and not self.has_on_add then
		self.has_on_add = true
		if self.on_add then
			self:on_add()
		end
		self:init_effect()
	end
end

function mt:disable()
	self.disable_count = self.disable_count + 1
	if not self.removed and self.added and self.disable_count == 1 and self.has_on_add then
		self.has_on_add = nil
		--自动清除的东西
		self:gc_clear()
		
		if self.on_remove then
			xpcall(self.on_remove, runtime['error_handle'], self)
		end
	end
end

---@type ac.unit
---@alias buffMap table<ac.buff, boolean>
---@return fun(s: buffMap, var: ac.buff):buffMap, ac.buff
function unit_class.__index:each_buff(name)
	if not self.buffs then
		return function () end
	end
	local buffs = {}
	for buff in pairs(self.buffs) do
		buffs[buff] = true
	end
	if not name then
		return pairs(buffs)
	end
	local next, s, var = pairs(buffs)

	---@param s table<ac.buff, boolean>
	---@param var ac.buff
	return function (s, var)
		while true do
			local r = next(s, var)
			if not r then return nil end
			if r.name == name then return r end
			var = r
		end
	end, s, var
end


--移除buff
function unit_class.__index:remove_buff(name)
	if not self.buffs then
		return
	end
	local tbl = {}
	for buff in pairs(self.buffs) do
		if buff.name == name then
			tbl[#tbl + 1] = buff
		end
	end
	for i = 1, #tbl do
		tbl[i]:remove()
	end
end

-- 找buff
function unit_class.__index:find_buff(name)
	if not self.buffs then
		return
	end
	for buff in pairs(self.buffs) do
		if buff.name == name then
			return buff
		end
	end
end

function mt:get_real_stack()
	local stack = 0
	local map = self.owner.buff_list
	if map == nil then 
		return 0
	end 
	local list = map[self.name]
	if list == nil then 
		return 0
	end 

	for i = 1, #list do 
		if i <= self.cover_max then 
			local buf = list[i] 
			if buf then 
				stack = stack + (buf.stack or 1) 
			end 
		else 
			break 
		end 
	end 
	return stack
end 

function mt:get_tip(tip, count)
	local stack = count or self:get_real_stack()
	tip = tip or self.tip 
	if not tip then return '' end
	local str = tip:gsub("'(.-)'", function(k)
		if k:sub(1, 1) == '*' then 
			k = k:sub(2, -1)
			count = stack
		else 
			count = 1
		end 
		local value = self[k]
		if value then 
			local tp = type(value)
			if tp == 'function' then
				value = value(self)
			end
			if type(value) == 'number' then 
				value = value * count
				local i, f = math.modf(value)
				local s 
				if f > 0 then 
					s = string.format('%.2f', value)
				else 
					s = string.format('%.f', value)
				end 
				return '|cffffff00' .. s .. '|r'
			else
				return '|cffffff00' .. value .. '|r'
			end 
			
		end 
	end)
	return str 
end 


function mt:get_art()
	return self.art 
end 


function mt:get_title()
	if self.title == nil then
		return 
	end 
	return self:get_tip(self.title)
end 



function mt:set_stack(stack)
	self.stack = stack
	self:fresh()
	if self.__abil_id then 

	end 
end 

function mt:add_stack(stack)
	self:set_stack(self:get_stack() + stack)
end 

function mt:get_stack()
	return self.stack or 0
end 


function mt:gc(target)
	--目前只支持两种自动回收
	--1为function，即属性
	--2为带有remove方法的对象，如特效，计时器，触发
	if not self._gc_target then
		self._gc_target = {}
	end
	table.insert(self._gc_target,target)
end

---@type table<string, ac.buff>
ac.buff = setmetatable({}, {
	__index = function (self, key)
		
		local obj = {}
		setmetatable(obj, buff)
	
		self[key] = obj
		return obj
	end,

	
	__call = function (self,tbl)
		if #tbl == 0 then
			return
		end
		local key = tbl[1]
		local object = self[key]
		for i=1,#tbl do
			local name = tbl[i]
			self[name] = object
		end
		return object
	end,
})

function buff.init()
	require 'types.buff.变形'
	require 'types.buff.冰冻'
	require 'types.buff.沉默'
	require 'types.buff.浮空'
	require 'types.buff.光环'
	require 'types.buff.护盾'
	require 'types.buff.击飞'
	require 'types.buff.减速'
	require 'types.buff.隐身'
	require 'types.buff.晕眩'
	require 'types.buff.弹道'
	require 'types.buff.生命周期'
	require 'types.buff.击退'
end


return buff